﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Gert3D
{
  public class Mesh
  {
    private List<Triangle> _faces = new List<Triangle>();

    public Mesh()
    {
    }

    public List<Triangle> Faces
    {
      get { return _faces; }
      set { _faces = value; }
    }

    public void Transform(Matrix transform)
    {
      foreach (Triangle tri in _faces)
      {
        tri.VertexA.Position = transform.Multiply(tri.VertexA.Position);
        tri.VertexB.Position = transform.Multiply(tri.VertexB.Position);
        tri.VertexC.Position = transform.Multiply(tri.VertexC.Position);
      }
    }

    private List<string> SplitString(string text, char separator, bool allowEmtpy)
    {
      List<string> result = new List<string>();
      StringBuilder token = new StringBuilder();

      for (int i = 0; i < text.Length; i++)
      {
        if (text[i] != separator)
          token.Append(text[i]);
        else
        {
          if (!allowEmtpy)
          {
            if (token.Length > 0)
              result.Add(token.ToString());
          }
          else
          {
            result.Add(token.ToString());
          }
          token.Length = 0;
        }
      }

      if (!allowEmtpy)
      {
        if (token.Length > 0)
          result.Add(token.ToString());
      }
      else
      {
        result.Add(token.ToString());
      }

      return result;
    }

    public bool FromObjFile(string fileName)
    {
      if (!File.Exists(fileName))
        throw new FileNotFoundException(fileName);

      List<Triangle> result = new List<Triangle>();
      List<Vector3> vertices = new List<Vector3>();
      List<Vector3> normals = new List<Vector3>();
      List<Vector3> texcoords = new List<Vector3>();

      StreamReader reader = new StreamReader(fileName);
      string line = reader.ReadLine();
      while (line != null)
      {
        line = line.ToLower().Trim();

        //Ignore comments
        if (line.StartsWith("#"))
        {
          line = reader.ReadLine();
          continue;
        }

        List<string> tokens = SplitString(line, ' ', false);
        if (tokens.Count <= 0)
        {
          line = reader.ReadLine();
          continue;
        }

        if (tokens[0] == "v")
        {
          if (tokens.Count != 4)
          {
            line = reader.ReadLine();
            continue;
          }

          Vector3 vertex = new Vector3();
          vertex.X = float.Parse(tokens[1].Replace('.', ','));
          vertex.Y = float.Parse(tokens[2].Replace('.', ','));
          vertex.Z = float.Parse(tokens[3].Replace('.', ','));

          vertices.Add(vertex);
        }
        else if (tokens[0] == "vt")
        {
          if (tokens.Count != 4)
          {
            line = reader.ReadLine();
            continue;
          }

          Vector3 vtex = new Vector3();
          vtex.X = float.Parse(tokens[1].Replace('.', ','));
          vtex.Y = float.Parse(tokens[2].Replace('.', ','));
          vtex.Z = float.Parse(tokens[3].Replace('.', ','));

          texcoords.Add(vtex);
        }
        else if (tokens[0] == "vn")
        {
          if (tokens.Count != 4)
          {
            line = reader.ReadLine();
            continue;
          }

          Vector3 normal = new Vector3();
          normal.X = float.Parse(tokens[1].Replace('.', ','));
          normal.Y = float.Parse(tokens[2].Replace('.', ','));
          normal.Z = float.Parse(tokens[3].Replace('.', ','));
          normal.Normalize();
          normals.Add(normal);
        }
        else if (tokens[0] == "f")
        {
          if (tokens.Count != 4)
          {
            line = reader.ReadLine();
            continue;
          }

          List<Vertex> points = new List<Vertex>();
          for (int i = 1; i < tokens.Count; i++)
          {
            List<string> items = SplitString(tokens[i], '/', true);

            int vi = int.Parse(items[0]) - 1;

            int vni = -1;
            if (items.Count >= 3)
              vni = int.Parse(items[2]) - 1;

            int vti = -1;
            if (items.Count >= 2)
              vti = int.Parse(items[1]) - 1;

            Vertex vertex = new Vertex();
            vertex.Position = vertices[vi];

            if (vni >= 0 && vni < normals.Count)
              vertex.Normal = normals[vni];
            else
              vertex.Normal = null;

            if (vti >= 0 && vti < texcoords.Count)
              vertex.TextureCoordinates = texcoords[vti];

            points.Add(vertex);
          }

          if (points.Count != 3)
          {
            line = reader.ReadLine();
            continue;
          }

          Triangle tri = new Triangle(points[2], points[1], points[0]);

          if (tri.VertexA.Normal == null || tri.VertexB.Normal == null || tri.VertexC.Normal == null)
          {
            tri.VertexA.Normal = tri.GetNormal();
            tri.VertexB.Normal = tri.GetNormal();
            tri.VertexC.Normal = tri.GetNormal();
          }

          result.Add(tri);
        }

        line = reader.ReadLine();
      }

      _faces = result;
      reader.Close();

      return true;
    }

  }
}
